+++++++++++++++++++++++++++++++++++++++++++
Un breve tutorial sui generatori di chiavi
+++++++++++++++++++++++++++++++++++++++++++
by .+MaLaTTiA.


//
  Nota introduttiva: questo tutorial, datato originariamente ottobre 1997, e' stato il
  mio primo tute pubblicato da +Fravia e la versione originale e' ancora presente, in
  lingua inglese, all'indirizzo http://www.fravia.org/nscekey.htm.
  Qua e la' sono stati aggiunti dei commenti... la traduzione comunque e' abbastanza
  affidabile, in quanto affidata... al sottoscritto =)
  Non fatevi ingannare dalla data di questo tute! Per il dovere che deve assolvere
  (spiegare lo studio di un algoritmo di codifica e la creazione di un generatore di
  chiavi) e' piu' che adatto allo scopo. Inoltre, per quanto ne so, i programmi citati
  utilizzano ancora lo stesso algoritmo con alcuni piccoli cambiamenti. Ricordate che
  in questo e in molti altri settori certe abitudini sono dure a morire! :)
//

[Netscape Cache Explorer]

Salve ragazzi, questo e' un breve tutorial su nsce. Questo programma e' un cache explorer,
che mostra tutti i siti che sono stati salvati nella cache di Netscape (c'e' una versione
anche per MSIE) divisi per dominio e consente di rivisitarli una volta offline. Io lo trovo
particolarmente utile perche' consente anche di salvare questi siti con tutte le immagini
e le pagine collegate ricostruendo automaticamente i link in esse presenti affinche'
funzionino in locale. Suppongo che siate riusciti a raggiungere questa porzione di codice
(ammetto di aver avuto qualche problema, a suo tempo, a trovare il punto giusto - eh, la
gioventu'! - anche se era proprio sotto il mio naso... ed era sufficiente utilizzare un
bpx messageboxa! :) GRAZIE +Zer0 e +ReZiDeNt per il vostro aiuto!). Ecco il codice che si
occupa del controllo:

:0041F88F 6A09                    push 00000009
:0041F891 8D45F4                  lea eax, dword ptr [ebp-0C]
:0041F894 50                      push eax    ** PUSH indirizzo della PW
:0041F895 FF7510                  push [ebp+10]

* Reference To: USER32.GetWindowTextA, Ord:0000h
                                  |
:0041F898 E84A560000              Call 00424EE7
:0041F89D 83F808                  cmp eax, 00000008 ** la pw e' lunga 8 caratteri?
:0041F8A0 0F85D5000000            jne 0041F97B      ** NO, salta
:0041F8A6 6A1F                    push 0000001F     ** SI, vai avanti
:0041F8A8 8D45D4                  lea eax, dword ptr [ebp-2C]  ** indirizzo del NOME
:0041F8AB 50                      push eax
:0041F8AC 6A70                    push 00000070
:0041F8AE 56                      push esi

//
  Breve nota. Non so se ve lo ricordate, ma la funzione API getdlgitemtexta funziona 
  cosi':

  Declare Function GetDlgItemText Lib "user32" Alias "GetDlgItemTextA" 
	(ByVal hDlg As Long, 
	 ByVal nIDDlgItem As Long, 
	 ByVal lpString As String, 
	 ByVal nMaxCount As Long) As Long

  la locazione che ci interessa (quella del NOME) e' esattamente il terzo parametro
  della chiamata alla funzione e, come tale, lo possiamo trovare come terzultima PUSH
  prima della CALL: quel "PUSH EAX" e' esattamente il terzo parametro della funzione
  getdlgitemtexta!!!
//


* Reference To: USER32.GetDlgItemTextA, Ord:0000h
                                  |
:0041F8AF E80B570000              Call 00424FBF  ** leggi il nome
:0041F8B4 85C0                    test eax, eax  ** nome=0 caratteri?
:0041F8B6 0F84BF000000            je 0041F97B    ** continua a chiedere il nome
:0041F8BC 8D45F4                  lea eax, dword ptr [ebp-0C]
:0041F8BF 50                      push eax       ** salva la PW
:0041F8C0 8D45D4                  lea eax, dword ptr [ebp-2C]
:0041F8C3 50                      push eax       ** salva il NOME
:0041F8C4 E887020000              call 0041FB50  ** PRIMA CALL IMPORTANTE
:0041F8C9 83C408                  add esp, 00000008
:0041F8CC 8D45F4                  lea eax, dword ptr [ebp-0C]
:0041F8CF 50                      push eax
:0041F8D0 E8FA020000              call 0041FBCF  ** SECONDA CALL IMPORTANTE
:0041F8D5 59                      pop ecx
:0041F8D6 A320204300              mov [00432020], eax  ** salva il valore
:0041F8DB E99B000000              jmp 0041F97B   ** torna al programma

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041F86F(C)
|
:0041F8E0 A120204300              mov eax, [00432020]  ** valore salvato in precedenza
:0041F8E5 3B0510204300            cmp eax, dword ptr [00432010]  ** IMPORTANTE!
:0041F8EB 743B                    je 0041F928    *** QUESTO E' IL SALTO BUONO!
:0041F8ED 6A50                    push 00000050 **** BAD GUY!!!
:0041F8EF 8D4584                  lea eax, dword ptr [ebp-7C]
:0041F8F2 50                      push eax
:0041F8F3 56                      push esi


Ok, diamo un'occhiata a queste righe di codice... innanzitutto c'e' un controllo relativo
alla password: se essa raggiunge gli 8 caratteri allora comincia l'algoritmo, altrimenti 
il controllo ritorna all'utente, che puo' inserire altri caratteri per la password. Nel
momento in cui la password raggiunge gli 8 caratteri, il programma legge il nome inserito
e fa DUE CHIAMATE MOLTO IMPORTANTI: la prima richiede 2 argomenti, ovvero gli indirizzi in
cui sono stati salvati il nome e la password, mentre la seconda richiede solo l'indirizzo
del nome. Dopo queste due chiamate il controllo viene restituito all'utente e, quando il
pulsante Ok viene selezionato, il programma salta all'indirizzo 41f8e0, dove controlla il
valore salvato con un altro valore salvato in precedenza alla locazione di memoria 432010.
Se date un'occhiata a questa locazione (a vostra scelta, utilizzando l'approccio "live"
con SoftIce o il metodo dead listing con Wdsam o IDA), potrete notare che questo valore
e' 190h, ovvero 400d.

//
  Altra nota: questo valore e' la chiave su cui si basa l'intero algoritmo di protezione.
  Essa cambia da una versione all'altra del programma, intenzionalmente in questa sede ho
  deciso di darvi una chiave vecchia... funziona, ma con una versione del programma che
  forse non troverete neanche piu' in giro! Tanto non vi interessa sproteggere il 
  programma, ma volete solo imparare le tecniche... vero? :)
//


Ora sappiamo che c'e' un algoritmo di codifica che utilizza sia il nome sia la pw per creare
un solo numero, che dev'essere per forza 400. Hmmm... allora in questo caso NON ABBIAMO la
password gia' pronta (ahivoi, in questo caso non funziona il trucco "data window" :)), ma
dobbiamo fare un patch del programma O creare un generatore di chiavi. Poiche' il modo piu'
semplice sembrava essere l'approccio "distruttivo", decisi di cambiare il "74 3b" alla
locazione di memoria 41f8eb in un "eb 3b" e provai a registrare il programma con una pw
qualsiasi: beh, la dialog box mi disse che la pw era giusta (adoro questo lavoro!)... ma
sfortunatamente il programma NON era registrato... argh... forse qualche altro controllo...
ma, essendo davvero TERRIBILMENTE pigro, ho deciso di cambiare approccio e cercare di dare
un'occhiata all'algoritmo di codifica... BAMBINI, NON FATELO A CASA!!! :) Questa e' una
PESSIMA pratica, nel cracking e nella programmazione! Proseguite sempre nella convinzione
delle vostre decisioni, come insegna +ORC! (piccolo inciso: "ah, quante volte in gioventu'
ci si e' trovati davanti ad esami in cui si cambiava esercizio di continuo, e alla fine non
si combinava niente! :)). Infatti, il lavoro non e' stato affatto semplice come speravo, ma
ho imparato un sacco da esso: quindi ho deciso di insegnarvi il modo in cui si prepara un
bellissimo generatore di chiavi, sperando che questo algoritmo venga utilizzato ancora :)
(un suggerimento: se date un'occhiata a MSIE cache explorer, noterete che esso utilizza lo
STESSO IDENTICO algoritmo, semplicemente con una chiave finale diversa: 192h anziche' 
190h... si sono sforzati, eh! ;)).

Ora parliamo dei generatori di chiavi: la prima cosa che bisogna fare e' leggere il codice
e cercare di capire esattamente cosa succede. Ora, diamo un'occhiata alla PRIMA delle due
call importanti, quella all'indirizzo 41fb50:

:0041FB50 55                      push ebp
:0041FB51 8BEC                    mov ebp, esp
:0041FB53 83C4F8                  add esp, FFFFFFF8
:0041FB56 53                      push ebx
:0041FB57 56                      push esi
:0041FB58 57                      push edi
:0041FB59 8B7508                  mov esi, dword ptr [ebp+08]
:0041FB5C 8BDE                    mov ebx, esi
:0041FB5E 56                      push esi

* Reference To: KERNEL32.lstrlenA, Ord:0000h
                                  |
:0041FB5F E8CF520000              Call 00424E33	
:0041FB64 83F808                  cmp eax, 00000008  ** eax=lunghezza del nome
:0041FB67 7305                    jnb 0041FB6E

* Possible Reference to String Resource ID=00008: "%d Object(s) selected"
                                  |
:0041FB69 B808000000              mov eax, 00000008

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FB67(C)
|
:0041FB6E 33C9                    xor ecx, ecx
:0041FB70 8D55F8                  lea edx, dword ptr [ebp-08] ** NUOVO INDIRIZZO

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FB7B(C)
|
:0041FB73 C60200                  mov byte ptr [edx], 00
:0041FB76 41                      inc ecx
:0041FB77 42                      inc edx
:0041FB78 83F908                  cmp ecx, 00000008
:0041FB7B 72F6                    jb 0041FB73

** Con questo ciclo il programma libera dello spazio a partire da un nuovo indirizzo in 
memoria "azzerando" le locazioni... uhmmm... perche'? Quando si usa il comando "mov" non
e' necessario liberare prima dello spazio... proseguiamo:

:0041FB7D 33C9                    xor ecx, ecx
:0041FB7F 3BC1                    cmp eax, ecx
:0041FB81 761A                    jbe 0041FB9D

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FB9B(C)
|
:0041FB83 8BD1                    mov edx, ecx
:0041FB85 83E207                  and edx, 00000007
:0041FB88 8D7C15F8                lea edi, dword ptr [ebp + edx - 08]
:0041FB8C 8A13                    mov dl, byte ptr [ebx]
:0041FB8E 0017                    add byte ptr [edi], dl

** Ecco perche'!!! Il programma non usa MOV, usa un ADD!!! :)

:0041FB90 43                      inc ebx
:0041FB91 803B00                  cmp byte ptr [ebx], 00
:0041FB94 7502                    jne 0041FB98
:0041FB96 8BDE                    mov ebx, esi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FB94(C)
|
:0041FB98 41                      inc ecx
:0041FB99 3BC1                    cmp eax, ecx
:0041FB9B 77E6                    ja 0041FB83

OK... cosa fa questo ciclo? L'istruzione alla locazione di memoria 41fb8c copia il valore 
della n-esima lettera della stringa nel registro DL, quindi questo valore viene aggiunto
nelle locazioni contenute in edi (queste sono esattamente le locazioni che erano state
azzerate in precedenza!). Questa procedura viene ripetuta finche' non si raggiungono 8
caratteri (se la stringa e' di lunghezza inferiore o uguale a 8 caratteri), o finche' TUTTI
i caratteri della stringa non vengono inseriti nello spazio in memoria, ricominciando dalla
prima locazione di memoria quando se ne raggiunge il fondo.

+Zer0     --> becomes in memory --> +Zer0+Ze
MaLaTTiA  -->       remains     --> MaLaTTiA
+ReZiDeNt --> becomes in memory --> *ReZiDeN

Il carattere rappresentato dall'asterisco corrisponde al valore ASCII 159, cioe' 43 ("+")
sommato a 116 ("t"). Ne risulta che alla fine di questa porzione di codice abbiamo una
stringa di 8 caratteri che e' stata costruita a partire dal nome che abbiamo inserito...
Vediamo cosa fa il programma adesso:

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FB81(C)
|
:0041FB9D 33C9                    xor ecx, ecx
:0041FB9F 8B450C                  mov eax, dword ptr [ebp+0C] ** INDIRIZZO DELLA PW
:0041FBA2 8BD8                    mov ebx, eax
:0041FBA4 8D75F8                  lea esi, dword ptr [ebp-08] ** NUOVA STRINGA

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FBC6(C)
|
:0041FBA7 33C0                    xor eax, eax
:0041FBA9 8A06                    mov al, byte ptr [esi] ** sposta l'n-esima lettera
:0041FBAB BF1A000000              mov edi, 0000001A
:0041FBB0 99                      cdq
:0041FBB1 F7FF                    idiv edi               ** al=al/1a  dl=dl%1a
:0041FBB3 80C241                  add dl, 41
:0041FBB6 2813                    sub byte ptr [ebx], dl
:0041FBB8 803B00                  cmp byte ptr [ebx], 00
:0041FBBB 7D03                    jge 0041FBC0
:0041FBBD 80031A                  add byte ptr [ebx], 1A

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FBBB(C)
|
:0041FBC0 41                      inc ecx
:0041FBC1 43                      inc ebx
:0041FBC2 46                      inc esi
:0041FBC3 83F908                  cmp ecx, 00000008
:0041FBC6 72DF                    jb 0041FBA7

Dopo il ciclo c'e' subito un'istruzione "ret": tutta la prima parte dell'algoritmo di 
codifica e' qui, quindi state attenti! Il programma prende l'n-esimo valore della stringa
appena creata, lo divide per il valore 1Ah (che corrisponde a 26d) ed inserisce il resto
in dl. Questo e' mostrato anche nel commento che ho inserito all'indirizzo 41fbb1: usando
la notazione C, abbiamo al=al/1a (divisione intera) e dl=dl%1a (resto della divisione
intera). In seguito, il valore 41h (65d) viene sommato a dl e il nuovo dl viene sottratto
dal carattere n-esimo DELLA PASSWORD INSERITA IN PRECEDENZA. Se il valore finale e' minore
di 0, allora viene aggiunto 1Ah (26d). Alla fine di questo ciclo, la procedura termina.

Ora tocca alla SECONDA call importante, all'indirizzo 41fbcf. Eccone la "versione 
completa":


:0041FBCF 55                      push ebp
:0041FBD0 8BEC                    mov ebp, esp
:0041FBD2 83C4F8                  add esp, FFFFFFF8
:0041FBD5 53                      push ebx
:0041FBD6 56                      push esi
:0041FBD7 57                      push edi
:0041FBD8 33FF                    xor edi, edi

* Reference To: KERNEL32.GetTickCount, Ord:0000h
                                  |
:0041FBDA E8EE510000              Call 00424DCD
:0041FBDF 8945FC                  mov dword ptr [ebp-04], eax
:0041FBE2 33F6                    xor esi, esi
:0041FBE4 8B4508                  mov eax, dword ptr [ebp+08]
:0041FBE7 8BD8                    mov ebx, eax

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FC2C(C)
|

* Reference To: KERNEL32.GetTickCount, Ord:0000h
                                  |
:0041FBE9 E8DF510000              Call 00424DCD
:0041FBEE 8945F8                  mov dword ptr [ebp-08], eax
:0041FBF1 33C0                    xor eax, eax
:0041FBF3 8A03                    mov al, byte ptr [ebx]
:0041FBF5 03C6                    add eax, esi

* Possible Reference to Dialog: DialogID_000A
                                  |

* Possible Reference to String Resource ID=00010: "(Empty)"
                                  |
:0041FBF7 B90A000000              mov ecx, 0000000A
:0041FBFC 99                      cdq
:0041FBFD F7F9                    idiv ecx
:0041FBFF 8BCA                    mov ecx, edx
:0041FC01 33C0                    xor eax, eax
:0041FC03 8A4301                  mov al, byte ptr [ebx+01]
:0041FC06 03C6                    add eax, esi
:0041FC08 40                      inc eax
:0041FC09 51                      push ecx

* Possible Reference to Dialog: DialogID_000A
                                  |

* Possible Reference to String Resource ID=00010: "(Empty)"
                                  |
:0041FC0A B90A000000              mov ecx, 0000000A
:0041FC0F 99                      cdq
:0041FC10 F7F9                    idiv ecx
:0041FC12 59                      pop ecx
:0041FC13 0FAFCA                  imul ecx, edx
:0041FC16 03F9                    add edi, ecx

* Reference To: KERNEL32.GetTickCount, Ord:0000h
                                  |
:0041FC18 E8B0510000              Call 00424DCD
:0041FC1D 2B45F8                  sub eax, dword ptr [ebp-08]
:0041FC20 C1E80A                  shr eax, 0000000A
:0041FC23 03C7                    add eax, edi
:0041FC25 8BF8                    mov edi, eax
:0041FC27 46                      inc esi
:0041FC28 43                      inc ebx
:0041FC29 83FE07                  cmp esi, 00000007
:0041FC2C 7CBB                    jl 0041FBE9

* Reference To: KERNEL32.GetTickCount, Ord:0000h
                                  |
:0041FC2E E89A510000              Call 00424DCD
:0041FC33 2B45FC                  sub eax, dword ptr [ebp-04]
:0041FC36 C1E80B                  shr eax, 0000000B
:0041FC39 03C7                    add eax, edi
:0041FC3B 5F                      pop edi
:0041FC3C 5E                      pop esi
:0041FC3D 5B                      pop ebx
:0041FC3E 59                      pop ecx
:0041FC3F 59                      pop ecx
:0041FC40 5D                      pop ebp
:0041FC41 C3                      ret

Tutte queste chiamate a GetTickCount sono una protezione: probabilmente, se utilizzassimo
un debugger "normale", il conto dei ticks sarebbe sbagliato e verrebbe restituito qualche
messaggio d'errore... ma noi non usiamo un debugger normale: usiamo Winice! (TADAAA!) :))
Quindi, in realta' ci interessa solo una "versione breve" di questa procedura... in
seguito a un'operazione di "sfoltimento" questo e' cio' che appare:


:0041FBE4 8B4508                  mov eax, dword ptr [ebp+08] ** indirizzo della pw modificata
:0041FBE7 8BD8                    mov ebx, eax

:HERE THE BIG LOOP STARTS:
:0041FBF3 8A03                    mov al, byte ptr [ebx] ** n-esimo valore
:0041FBF5 03C6                    add eax, esi           ** inizialmente 0
:0041FBF7 B90A000000              mov ecx, 0000000A
:0041FBFC 99                      cdq
:0041FBFD F7F9                    idiv ecx               ** eax=eax/10
:0041FBFF 8BCA                    mov ecx, edx           ** ecx=edx=edx%10
:0041FC01 33C0                    xor eax, eax           ** eax=0
:0041FC03 8A4301                  mov al, byte ptr [ebx+01] **(n+1)esimo valore
:0041FC06 03C6                    add eax, esi           ** ancora 0
:0041FC08 40                      inc eax            ** eax=(n+1)esimo valore+1
:0041FC09 51                      push ecx           ** salva il primo resto
:0041FC0A B90A000000              mov ecx, 0000000A
:0041FC0F 99                      cdq
:0041FC10 F7F9                    idiv ecx           ** eax=eax/10
:0041FC12 59                      pop ecx            ** carica il primo resto
:0041FC13 0FAFCA                  imul ecx, edx      ** ecx=primo resto*secondo resto
:0041FC16 03F9                    add edi, ecx       ** edi=edi+ecx 
                                                        EDI CRESCE!
:0041FC20 C1E80A                  shr eax, 0000000A  ** eax diventa 0
:0041FC23 03C7                    add eax, edi
:0041FC25 8BF8                    mov edi, eax       ** nessun cambiamento :)
:0041FC27 46                      inc esi
:0041FC28 43                      inc ebx
:0041FC29 83FE07                  cmp esi, 00000007  ** ho scandito tutta la pw?
:0041FC2C 7CBB                    jl 0041FBE9        ** no. Salta all'inizio.
:HERE THE BIG LOOP ENDS:

:0041FC36 C1E80B                  shr eax, 0000000B
:0041FC39 03C7                    add eax, edi       ** ax=valore finale

Come potete vedere, l'algoritmo dopotutto non e' COSI' difficile da comprendere... magari
sara' piu' complicato fare il generatore di chiavi, ma ora possiamo ancora capire cosa
sta succedendo. Il programma raccoglie i valori n-esimo e (n+1)-esimo della pw modificata,
li divide per 10, quindi moltiplica il resto del valore n-esimo con quello del valore
(n+1)-esimo incrementato di 1. Se date un'occhiata alle ultime righe, potete notare che
il valore finale di questa somma viene salvato in eax e, se guardate la porzione di codice
che compare subito dopo la call potete notare QUESTA riga:

:0041F8D6 A320204300              mov [00432020], eax  ** salva il valore

che SALVA IL VALORE IN EAX per effettuare una successica "cmp". Quindi, QUESTO e' il valore
che dev'essere uguale a 190h, cioe' 400d. Ecco il modo in cui tale numero viene costruito:


r1*r2 + r2*r3 + r3*r4 + r4*r5 + r5*r6 + r6*r7 + r7*r8 =190h=400d

dove

r1=p1+0%10
r2=p2+1%10
r3=p3+2%10
r4=p4+3%10
r5=p5+4%10
r6=p6+5%10
r7=p7+6%10
r8=p8+7%10

e dove px e' l'x-esimo elemento della password modificata, cioe':

(valore dell'x-esimo elemento della password originale-((x-esimo elemento del nome "scritto
su se stesso")%26)+65)+26 (se il valore e' <0).

Un casino, dite? Beh, non avete mai visto camera mia :)
Comunque lo ammetto, questa situazione e' abbastanza imbarazzante, specialmente se vogliamo
cercare di invertire la "funzione" (e noi lo vogliamo!): questo perche' una funzione di
questo tipo, ovvero che fa uso dell'operatore resto, puo' avere un numero infinito di
soluzioni (ad esempio, il resto "5" di una divisione per 10 puo' essere stato generato dai 
numeri "5", "15", "25" e cosi' via). Tutto cio' che dobbiamo fare, a questo punto, e' usare
i nostri cervelli... dobbiamo creare _DA_ZERO_ UNA delle INFINITE soluzioni, possibilmente
la piu' semplice per noi! :)

//
  Nota: in realta' non sono infinite - i numeri in gioco NON sono infiniti - pero' sono
  tante, abbastanza da occupare tutti i nostri futuri weekend liberi :)
//

D'ora in poi, leggerete LA MIA soluzione, ma la vostra puo' essere diversa dalla mia...
Se avete qualche idea che pensate sia migliore delle mie, PER FAVORE fatemelo sapere,
anche io sono qui per imparare! Potete vedere il mio indirizzo email alla fine del
testo.
Ok, la PRIMA cosa che dobbiamo fare e' buttare giu' alcune idee:
1) Dobbiamo ridurre il numero di parametri che cambiano.
2) Dobbiamo costruire quel dannato numero 400d.
3) Dobbiamo fare un generatore di chiavi funzionante :)

A questo punto, ho deciso di cominciare dal fondo: il numero 400. Come viene costruito?
Beh, lo potete vedere voi stessi dalle righe precedenti... e' la somma di sette prodotti
in cui il secondo membro e' il primo membro del prodotto successivo. Quindi, cerchiamo
di dare alcuni valori a quegli rx per creare il numero giusto. QUESTO e' un casino... ed
ecco che entra in gioco lo Zen :)
Ho pensato: "Ehi, questi programmatori sono esseri umani come me, probabilmente avranno
pensato a qualche numero SEMPLICE"... inoltre, i numeri da r2 a r7 vengono moltiplicati
DUE volte, quindi probabilmente i programmatori avranno trovato piu' semplice utilizzare
delle potenze di 2. Quindi:

1) Usa numeri facili/moltiplica 2 volte --> Ripeti lo STESSO numero il piu' volte possibile
2) Potenze di due --> parti con la potenza di 2 piu' grande che sia pero' <10, ovvero 8

In questo modo ho scoperto che il numero 400 e' costituito da (8*8)*6 + (8*2). La riga
scritta in precedenza diventa:

08*08 + 08*08 + 08*08 + 08*08 + 08*08 + 08*08 + 08*02 =190h=400d
r1*r2 + r2*r3 + r3*r4 + r4*r5 + r5*r6 + r6*r7 + r7*r8 =190h=400d

E:
r1=p1+0%10=8  -> p1=8
r2=p2+1%10=8  -> p2=7
r3=p3+2%10=8  -> p3=6
r4=p4+3%10=8  -> p4=5
r5=p5+4%10=8  -> p5=4
r6=p6+5%10=8  -> p6=3
r7=p7+6%10=8  -> p7=2
r8=p8+7%10=2  -> p8=5 (5+7=12 ; 12%10=2)

Ora conosciamo i valori che la pw modificata dovrebbe avere. Ma cosa dire della password
ORIGINALE? Da cosa e' costituita? Numeri? Lettere? Ora dobbiamo semplificare il nostro
lavoro il piu' possibile: in un primo momento avevo pensato di utilizzare semplicemente
dei numeri, ma ho notato che, se chiamiamo "x" il valore di un carattere della pw e "c"
il valore di un carattere del nome "ripiegato su se stesso", si ottiene:

p=(x-(65+c%26)(+26?))%10

e, se x e' un numero (ascii 48-57) e io sottraggo ad essoun valore compreso fra 65 e 65+26,
anche se ad esso aggiungessi sempre 26 il valore risultante sarebbe talvolta maggiore di 0
e talvolta minore di 0. MALE: se devo lavorare con dei resti non voglio che fra essi ci
siano dei "salti" come quello fra 0 (resto 0) e -1 (=255, resto 255). Per questo motivo ho
deciso di utilizzare per la password le lettere dalla a alla z, in modo da avere SEMPRE dei
numeri positivi e non dover neanche avere a che fare con quel (26?) :)
Quindi, le p vengono costruite grazie alla seguente equazione:

p=(x-(65+c%26))%10

Benissimo, ho risolto un problema ma ora ne ho un altro: come posso fare in modo che le "p"
siano esattamente come quelle di cui ho bisogno? Infatti, se ho bisogno di un resto, sia la
lettera n-esima sia la (n+10)-esima possono andare bene. Ho deciso di cominciare da una pw
costituita da sole "a", quindi calcolare i suoi valori "p", quindi aggiungere la 
"differenza" fra i "p" che ho calcolato e quelli di cui ho bisogno. Proviamo con un nome
semplice: MaLaTTiA :)

1) Ripiega la stringa         M      a     L     a     T     T     i     A
   (qui non e' necessario)
2) Dec                       77     97    76    97    84    84   105    65
3) p=(97-(65+c%26))%10       07     03    08    03    06    06    01    09
   (97 e' "a")
4) Valori richiesti          08     07    06    05    04    03    02    05
5) Aggiungi ad "a"           01     04    08    02    08    07    01    06
6) PASSWORD:                  b      e     i     c     i     h     b     g

Ehi... FUNZIONA!!! :)

Questo e' tutto. Ora, ecco il sorgente C per questo generatore di chiavi (abbiate pazienza,
il mio C non e' un granche'... ;)

*****************************************************************************
#include 

unsigned char s[8]={0,0,0,0,0,0,0,0};
char string [80];
int values[8], required[8]={8,7,6,5,4,3,2,5};
int i,j,ok=0,ok2=0;

void main (){
    printf ("\n...//\\Oo/\\\\ Netscape Cache Explorer v1.26 KeyGen - by
    .MaLaTTiA. //\\Oo/\\\\...\n\n");
    printf ("\nUser name: ");
    gets (string);
    i=j=0;
    if (string[0]==0) exit(0);
    while ((!ok)||(!ok2)){
        if (string[j]==0){
            if (!ok2) j=0;
            ok=1;
        }
        s[i]=s[i]+string[j];
        j++;i++;
        if (i>7){
            ok2=1;
            i=0;
        }
    }
    for (i=0;i<=7;i++){
        values[i]=required[i]-((32-(s[i]%26))%10);
        if (values[i]<0) values[i]=values[i]+10;
    }
    printf ("Key: ");
    for (i=0;i<=7;i++){
        printf ("%c", 97+values[i]);
    }
    printf ("\n");
}
*****************************************************************************

Noto ora che non e' neanche ottimizzato... bah, funziona! :)) Credo che questo sorgente sia
abbastanza semplice da comprendere, tuttavia se non comprendete qualcosa mandatemi una mail
e saro' felice di aiutarvi! Ora, potete anche costruire il generatore di chiavi per MSICE,
tenendo presente che il valore necessario non e' 190h ma e' 192h. Provatelo, l'algoritmo e'
lo stesso! :)

byez,

  .+MaLaTTiA. (malattia@usa.net)